using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/// <summary>
/// OBB 投射
/// </summary>
public sealed class BoxHit : IHitCheckStrategy
{
    private readonly Vector3 m_HalfExtents;   // 盒体半尺寸
    private readonly Quaternion m_Orientation; // 盒体旋转

    public BoxHit(Vector3 halfExtents, Quaternion orientation = default)
    {
        m_HalfExtents = halfExtents;
        m_Orientation = orientation == default ? Quaternion.identity : orientation;
    }

    public List<Entity> Check(Entity entity, Vector3 origin, Vector3 dir, float maxDist)
    {
        // 1. 构建盒体 8 个顶点
        var vertices = GetBoxVertices(origin, m_HalfExtents, m_Orientation);
        // 2. 沿dir 方向扫掠
        var sweptVertices = vertices.Select(v => v + dir * maxDist).ToArray();
        // 3. 收集与盒体相交的单位
        var enemys = entity.AttackComponent.FindEnemys();
        return enemys
            .Where(u => !u.HealthComponent.IsDead)
            .Where(u => PointInBox(u.transform.position, vertices, sweptVertices))
            .ToList();
    }

    // ---------- 辅助 ----------
    private static Vector3[] GetBoxVertices(Vector3 center, Vector3 half, Quaternion rot)
    {
        Vector3[] v = new Vector3[8];
        Vector3[] dirs =
        {
            new(-half.x, -half.y, -half.z),
            new(+half.x, -half.y, -half.z),
            new(+half.x, +half.y, -half.z),
            new(-half.x, +half.y, -half.z),
            new(-half.x, -half.y, +half.z),
            new(+half.x, -half.y, +half.z),
            new(+half.x, +half.y, +half.z),
            new(-half.x, +half.y, +half.z)
        };
        for (int i = 0; i < 8; i++) v[i] = center + rot * dirs[i];
        return v;
    }

    private static bool PointInBox(Vector3 p, Vector3[] start, Vector3[] end)
    {
        // 简化为 12 条边的线段点距离测试
        for (int i = 0; i < 12; i++)
        {
            var (a, b) = GetEdge(i, start, end);
            if (SegmentPointDistance(a, b, p) < 0.5f) return true;
        }
        return false;
    }

    private static (Vector3, Vector3) GetEdge(int idx, Vector3[] s, Vector3[] e)
    {
        int[] edges = { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 };
        int a = edges[idx * 2], b = edges[idx * 2 + 1];
        return (s[a], e[b]);
    }

    private static float SegmentPointDistance(Vector3 a, Vector3 b, Vector3 p)
    {
        Vector3 ab = b - a;
        float t = Mathf.Clamp01(Vector3.Dot(p - a, ab) / Vector3.SqrMagnitude(ab));
        Vector3 proj = a + ab * t;
        return Vector3.Distance((p - proj), Vector3.zero);
    }
}